﻿using ApiTemplateGenerate.Extend;
using ApiTemplateGenerate.ModelInfo;
using ApiTemplateGenerate.TypeConvert;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using RazorEngine.Templating;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace ApiTemplateGenerate
{
    public class Api
    {
        public ModelInfo.TypeEnum TypeEnum { get; set; }
        /// <summary>
        /// 描述
        /// </summary>
        public string Describe { get; set; }
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 类型
        /// </summary>
        public string Type { get; set; }
        /// <summary>
        /// 值
        /// </summary>
        public object Vlaue { get; set; }
        /// <summary>
        /// 是否为空
        /// </summary>
        public bool IsNull { get; set; }
    }
    public class ApiToJsMiddleware
    {

        private readonly RequestDelegate _next;
        private readonly IApiDescriptionGroupCollectionProvider _IApiDescriptionGroupCollectionProvider;
        public ApiToJsMiddleware(IApiDescriptionGroupCollectionProvider apiDescriptionGroupCollectionProvider, RequestDelegate next)
        {
            _IApiDescriptionGroupCollectionProvider = apiDescriptionGroupCollectionProvider;
            _next = next;
        }
        public async Task Invoke(HttpContext httpContext)
        {
            try
            {
                List<ApiInfo> apiInfos = new List<ApiInfo>();
                var groups = _IApiDescriptionGroupCollectionProvider.ApiDescriptionGroups.Items;
                foreach (var item in groups)
                {
                    foreach (var item2 in item.Items)
                    {
                        ApiInfo apiInfo = new ApiInfo();
                        //apiInfo.Describe
                        apiInfo.ApiUrl = item2.RelativePath;
                        apiInfo.ControllerName = ((Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)item2.ActionDescriptor).ControllerName;
                        apiInfo.ActionName = ((Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)item2.ActionDescriptor).ActionName;
                        apiInfo.RequestMethod = item2.HttpMethod;
                        List<ParameterInfo> parameterInfos = new List<ParameterInfo>();
                        foreach (var parameterDescription in item2.ParameterDescriptions)
                        {
                            //parameterDescription.Type
                            parameterInfos.Add(new ParameterInfo
                            {
                                DefaultValue = parameterDescription.DefaultValue,
                                IsRequired = parameterDescription.IsRequired,
                                Name = parameterDescription.Name,
                                ParameteType = parameterDescription.Type,
                                SourceType = DicCommon.SourceTypeDic[parameterDescription.Source.DisplayName]
                            });
                        }
                        apiInfo.ParameterInfos = parameterInfos;
                        ReturnInfo returnInfo = new ReturnInfo();
                        if (item2.SupportedResponseTypes.Count > 0)
                        {
                            var returnRrsult = item2.SupportedResponseTypes[0];
                            returnInfo.ReturnType = returnRrsult.Type;
                            returnInfo.ReturnValue = string.Empty;
                            apiInfo.ReturnInfo = returnInfo;
                        }
                        apiInfos.Add(apiInfo);
                    }
                }

                List<ApiDescribe> apiDescribes = new List<ApiDescribe>();

                #region 注释
                //foreach (var apiInfo in apiInfos)
                //{
                //    ApiDescribe apiDescribe = new ApiDescribe();
                //    apiDescribe.ApiUrl = apiInfo.ApiUrl;
                //    apiDescribe.Describe = apiInfo.Describe;
                //    apiDescribe.MethodName = apiInfo.ActionName;
                //    apiDescribe.RequestMethod = apiInfo.RequestMethod;
                //    apiDescribe.ReturnValue = apiInfo.ReturnInfo.ReturnValue;
                //    apiDescribe.IsNullParam = apiInfo.ParameterInfos.Count == 0;
                //    StringBuilder ParameterString = new StringBuilder();
                //    StringBuilder Params = new StringBuilder();
                //    Params.Append("{");
                //    bool isPath = false;
                //    foreach (var item in apiInfo.ParameterInfos)
                //    {
                //        ParameterString.Append($"{item.Name},");
                //        switch (item.SourceType)
                //        {
                //            case SourceType.Query:
                //                apiDescribe.IsParams = true;
                //                Params.Append($"{item}:{item},");
                //                break;
                //            case SourceType.Path:
                //                if (!isPath) { apiDescribe.ApiUrl = apiDescribe.ApiUrl.Replace("{", "${");isPath = true; };

                //                break;
                //            case SourceType.Body:
                //                apiDescribe.IsData = true;
                //                apiDescribe.Data = item.Name;
                //                break;
                //            default:
                //                break;
                //        }
                //    }
                //    if (ParameterString.Length > 0)
                //    {
                //        ParameterString.Remove(ParameterString.Length - 1, 1);
                //    }
                //    if (Params.Length > 1)
                //    {
                //        Params.Remove(Params.Length - 1, 1);
                //        Params.Append("}");
                //    }
                //    apiDescribe.Params = Params.ToString();
                //    apiDescribe.ParameterString = ParameterString.ToString();
                //    apiDescribes.Add(apiDescribe);
                //}
                #endregion

                //Console.WriteLine(resultString);

                var fileName = apiInfos.GroupBy(e => e.ControllerName).ToList();
                var content = new StringBuilder();
                foreach (var item in fileName)
                {
                    content.Clear();
                    apiDescribes = new List<ApiDescribe>();
                    TsModelDescribes = new List<TsModelDescribe>();
                    TsEnumDescribes = new List<TsEnumDescribe>();
                    foreach (var apiInfo in apiInfos.Where(e => e.ControllerName == item.Key))
                    {
                        //content.Clear();
                        ApiDescribe apiDescribe = new ApiDescribe
                        {
                            ApiUrl = apiInfo.ApiUrl,
                            Describe = apiInfo.Describe,
                            MethodName = apiInfo.ActionName,
                            RequestMethod = apiInfo.RequestMethod,
                            ReturnValue = apiInfo.ReturnInfo?.ReturnValue,
                            IsNullParam = apiInfo.ParameterInfos.Count == 0
                        };
                        StringBuilder ParameterString = new StringBuilder();
                        StringBuilder Params = new StringBuilder();
                        Params.Append("{");
                        var describes = new StringBuilder();
                        describes.Append("/**");
                        describes.Append($"* {apiInfo.Describe}");
                        bool isPath = false;
                        foreach (var parameterInfo in apiInfo.ParameterInfos)
                        {
                            var result=TsAnalysisType(parameterInfo.ParameteType.GetTypeInfo());
                            describes.Append($"* @param {parameterInfo.Name} {result.Type}");
                            ParameterString.Append($"{parameterInfo.Name},");
                            switch (parameterInfo.SourceType)
                            {
                                case SourceType.Query:
                                    apiDescribe.IsParams = true;
                                    Params.Append($"{parameterInfo.Name}:{parameterInfo.Name},");
                                    break;
                                case SourceType.Path:
                                    if (!isPath) { apiDescribe.ApiUrl = apiDescribe.ApiUrl.Replace("{", "${"); isPath = true; };

                                    break;
                                case SourceType.Body:
                                    apiDescribe.IsData = true;
                                    apiDescribe.Data = parameterInfo.Name;
                                    break;
                                default:
                                    break;
                            }
                        }
                       
                        if (ParameterString.Length > 0)
                        {
                            ParameterString.Remove(ParameterString.Length - 1, 1);
                        }
                        if (Params.Length > 1)
                        {
                            Params.Remove(Params.Length - 1, 1);
                            Params.Append("}");
                        }
                        apiDescribe.Params = Params.ToString();
                        apiDescribe.ParameterString = ParameterString.ToString();
                       

                        #region 分析类型
                        TypeExtend.ClassNames = new HashSet<string>();
                        if (apiInfo.ReturnInfo != null)
                        {   
                            var rssult=TsAnalysisType(apiInfo.ReturnInfo.ReturnType.GetTypeInfo());
                            describes.Append($" * @returns {rssult.Type}");
                            describes.Append($" */");
                            //TypeExtend.ClassNames = new HashSet<string>();
                        }
                       
                        foreach (var item3 in apiInfo.ParameterInfos)
                        {
                            //TypeExtend.ClassNames = new HashSet<string>();
                            TsAnalysisType(item3.ParameteType.GetTypeInfo());
                            TypeExtend.ClassNames = new HashSet<string>();
                        }
                        apiDescribe.Describes = describes.ToString();
                        apiDescribes.Add(apiDescribe);
                        #endregion
                    }
                    var resultString1 = RazorEngine.Engine.Razor.RunCompile(ApiTemplateGenerate.TemplateJsAxios.TemplateMethod.Template, "templateKeyMethod", null, apiDescribes);
                    resultString1 = resultString1.Replace("&lt;", "<");
                    resultString1 = resultString1.Replace("&gt;", ">");
                    content.Append(resultString1);
                    //接口生成
                    foreach (var interfaceDescribe in TsModelDescribes.DistinctEx(e => new { e.ClassName }))
                    {
                        var resultString = RazorEngine.Engine.Razor.RunCompile(ApiTemplateGenerate.TemplateJsAxios.TemplateInterface.Template, "templateKeyInterface", null, interfaceDescribe);
                        resultString = resultString.Replace("&lt;", "<");
                        resultString = resultString.Replace("&gt;", ">");
                        content.Append(resultString);
                    }
                    //枚举生成
                    TsEnumDescribes = TsEnumDescribes.DistinctEx(e => new { e.Name }).ToList();

                    var resultString2 = RazorEngine.Engine.Razor.RunCompile(ApiTemplateGenerate.TemplateJsAxios.TemplateEnum.Template, "templateKeyEnum", null, TsEnumDescribes);
                    resultString2 = resultString2.Replace("&lt;", "<");
                    resultString2 = resultString2.Replace("&gt;", ">");
                    content.Append(resultString2);


                    GenerateFile($"D://apiFile1/Demo/", $"/{item.Key}.js", content.ToString());
                }
                await _next(httpContext);
            }
            catch (Exception ex)
            {
                httpContext.Response.ContentType = "application/problem+json";

                var title = "An error occured: " + ex.Message;
                var details = ex.ToString();

                var problem = new
                {
                    Status = 500,
                    Title = title,
                    Detail = details
                };

                //Serialize the problem details object to the Response as JSON (using System.Text.Json)
                var stream = httpContext.Response.Body;
                await JsonSerializer.SerializeAsync(stream, problem);
            }
        }
        /// <summary>
        /// 写入磁盘
        /// </summary>
        /// <param name="directoryPath">目录</param>
        /// <param name="path">文件</param>
        /// <param name="content">内容</param>
        private static void GenerateFile(string directoryPath, string path, string content)
        {

            if (File.Exists(directoryPath + path))
            {
                File.Delete(directoryPath + path);
            }
            if (!Directory.Exists(directoryPath))
            {
                Directory.CreateDirectory(directoryPath);
            }


            //创建文本文件Demo.txt，并写入初始字符串"123456789"
            using (var fileStream = new FileStream(directoryPath + path, FileMode.CreateNew))
            {
                var result = content.Replace(";", "\r\n");
                byte[] data = System.Text.Encoding.UTF8.GetBytes(result);//
                fileStream.Write(data, 0, data.Length);
            }
        }
        /// <summary>
        /// 接口
        /// </summary>
        private static List<TsModelDescribe> TsModelDescribes { get; set; } = new List<TsModelDescribe>();
        /// <summary>
        /// 枚举
        /// </summary>
        private static List<TsEnumDescribe> TsEnumDescribes { get; set; } = new List<TsEnumDescribe>();
        /// <summary>
        /// 填充类和接口
        /// </summary>
        /// <param name="typeInfo"></param>
        /// <returns></returns>
        private Api TsAnalysisType(ApiTemplateGenerate.ModelInfo.TypeInfo typeInfo)
        {
            //Console.WriteLine(typeInfo.Name);
            switch (typeInfo.TypeEnum)
            {
                case ApiTemplateGenerate.ModelInfo.TypeEnum.Class:
                    {
                        var result = new TsModelDescribe();
                        result.ClassDescribe = typeInfo.Describe;
                        result.ClassName = typeInfo.Type.Name;

                        foreach (var item in typeInfo.TypeInfoChild)
                        {
                            var tsModelPropertyDescribe = new TsModelPropertyDescribe();
                            var resultChild = TsAnalysisType(item);
                            tsModelPropertyDescribe.IsNull = resultChild.IsNull;
                            tsModelPropertyDescribe.PropertyDescribe = resultChild.Describe;
                            tsModelPropertyDescribe.PropertyName = resultChild.Name;
                            tsModelPropertyDescribe.PropertyType = resultChild.Type;
                            result.ModelPropertyDescribes.Add(tsModelPropertyDescribe);
                        }
                        TsModelDescribes.Add(result);
                        return new Api { Describe = typeInfo.Describe, IsNull = false, Name = typeInfo.Name, Type = typeInfo.Type.Name, Vlaue = typeInfo.Value,TypeEnum=ModelInfo.TypeEnum.Class };
                    }
                case ApiTemplateGenerate.ModelInfo.TypeEnum.BaseType:
                    return new Api { Describe = typeInfo.Describe, IsNull = false, Name = typeInfo.Name, Type = TsType.TsTypeDic[typeInfo.Type.Name], Vlaue = typeInfo.Value, TypeEnum = ModelInfo.TypeEnum.BaseType };
                case ApiTemplateGenerate.ModelInfo.TypeEnum.IEnumerable:
                    {
                        var result = new TsModelDescribe();
                        result.ClassDescribe = typeInfo.Describe;
                        result.ClassName = typeInfo.Name;
                        var type = "any";
                        var isStart = true;
                        foreach (var item in typeInfo.TypeInfoChild)
                        {
                            var resultChild = TsAnalysisType(item);
                            if (isStart)
                            {
                                type = resultChild.Type;
                            }
                            isStart = false;
                        }
                        //TsModelDescribes.Add(result);
                        return new Api { Describe = typeInfo.Describe, IsNull = false, Name = typeInfo.Name, Type = $"Array<{type}>", Vlaue = typeInfo.Value, TypeEnum = ModelInfo.TypeEnum.IEnumerable };
                    }
                case ApiTemplateGenerate.ModelInfo.TypeEnum.IsArray:
                    return new Api { Describe = typeInfo.Describe, IsNull = false, Name = typeInfo.Name, Type = $"Array<{TsType.TsTypeDic[typeInfo.Type.Name.Replace("[]", string.Empty)]}>", Vlaue = typeInfo.Value, TypeEnum = ModelInfo.TypeEnum.IsArray };
                case ApiTemplateGenerate.ModelInfo.TypeEnum.Generic:
                    {
                        var result = new TsModelDescribe();
                        result.ClassDescribe = typeInfo.Describe;
                        result.ClassName = $"{typeInfo.Type.Name.Replace("`1", string.Empty)}<T>";
                        var type = "any";
                        foreach (var item in typeInfo.TypeInfoChild)
                        {
                           
                            var tsModelPropertyDescribe = new TsModelPropertyDescribe();
                            var resultChild = TsAnalysisType(item);
                            tsModelPropertyDescribe.IsNull = resultChild.IsNull;
                            tsModelPropertyDescribe.PropertyDescribe = resultChild.Describe;
                            tsModelPropertyDescribe.PropertyName = resultChild.Name;
                            tsModelPropertyDescribe.PropertyType = resultChild.Type;
                            if (item.IsT)
                            {
                                type = item.Type.Name;
                                if (resultChild.TypeEnum == ModelInfo.TypeEnum.IEnumerable)
                                {
                                    tsModelPropertyDescribe.PropertyType = "Array<T>";
                                    type = resultChild.Type.Replace("Array<", "").Replace(">", "");
                                }
                                //else if (resultChild.TypeEnum == ModelInfo.TypeEnum.Generic)
                                //{
                                //    tsModelPropertyDescribe.PropertyType = T;
                                //}
                                else
                                {
                                    tsModelPropertyDescribe.PropertyType = item.IsT ? "T" : resultChild.Type;
                                }

                            }
                            result.ModelPropertyDescribes.Add(tsModelPropertyDescribe);
                        }
                        TsModelDescribes.Add(result);
                        return new Api { Describe = typeInfo.Describe, IsNull = false, Name = typeInfo.Name, Type = $"{typeInfo.Type.Name.Replace("`1", string.Empty)}<{type}>", Vlaue = typeInfo.Value, TypeEnum = ModelInfo.TypeEnum.Generic };
                    }
                case ApiTemplateGenerate.ModelInfo.TypeEnum.Enum:
                    {
                        var result = new TsEnumDescribe();
                        result.Describe = typeInfo.Describe;
                        result.Name = typeInfo.Type.Name;
                        List<TsEnumFiledDescribe> tsEnumFiledDescribe = new List<TsEnumFiledDescribe>();
                        foreach (var item in typeInfo.TypeInfoChild)
                        {
                            tsEnumFiledDescribe.Add(new TsEnumFiledDescribe
                            {
                                Describe = item.Describe,
                                EnumName = item.Name,
                                Value = item.Value
                            });
                        }
                        result.TsEnumFiled = tsEnumFiledDescribe;
                        TsEnumDescribes.Add(result);
                    }
                    return new Api { Describe = typeInfo.Describe, IsNull = false, Name = typeInfo.Type.Name, Type = typeInfo.Name, Vlaue = typeInfo.Value, TypeEnum = ModelInfo.TypeEnum.Enum };
                case ApiTemplateGenerate.ModelInfo.TypeEnum.IsNullType:
                    var typeName = string.Empty;
                    if (typeInfo.TypeInfoChild.Count > 0)
                    {
                        typeName = TsType.TsTypeDic[typeInfo.TypeInfoChild[0].Type.Name];
                    }
                    return new Api { Describe = typeInfo.Describe, IsNull = true, Name = typeInfo.Name, Type = typeName, Vlaue = typeInfo.Value, TypeEnum = ModelInfo.TypeEnum.IsNullType };
                case ApiTemplateGenerate.ModelInfo.TypeEnum.Other:
                    return new Api { Describe = typeInfo.Describe, IsNull = false, Name = typeInfo.Name, Type = typeInfo.Name, Vlaue = typeInfo.Value, TypeEnum = ModelInfo.TypeEnum.Other };
                default:
                    return new Api { Describe = typeInfo.Describe, IsNull = false, Name = typeInfo.Name, Type = typeInfo.Name, Vlaue = typeInfo.Value, TypeEnum = ModelInfo.TypeEnum.Other };
            }
        }

    }


    /// <summary>
    /// 接口属性描述
    /// </summary>
    public class ModelPropertyDescribe
    {
        /// <summary>
        /// 属性名
        /// </summary>
        public string PropertyName { get; set; }
        /// <summary>
        /// 属性描述
        /// </summary>
        public string PropertyDescribe { get; set; }
        /// <summary>
        /// 属性类型
        /// </summary>
        //public ModelType PropertyTypeEnum { get; set; }
        /// <summary>
        /// 属性类型
        /// </summary>
        public string PropertyType { get; set; }
        /// <summary>
        /// 是否是可空类型
        /// </summary>
        public bool IsNull { get; set; } = false;
        ///// <summary>
        ///// 一个属性是数组之类的
        ///// </summary>
        //public IEnumerable<ModelDescribe> ModelDescribes { get; set; }
    }
    public class ApiDescribe
    {
        /// <summary>
        /// 是否是空参数
        /// </summary>
        public bool IsNullParam { get; set; } = false;
        /// <summary>
        /// 是否是params参数
        /// </summary>
        public bool IsParams { get; set; } = false;
        /// <summary>
        /// params参数
        /// </summary>

        public string Params { get; set; }
        /// <summary>
        /// 是否是Data参数
        /// </summary>
        public bool IsData { get; set; } = false;
        /// <summary>
        /// data 参数
        /// </summary>
        public string Data { get; set; }
        /// <summary>
        /// 方法名
        /// </summary>
        public string MethodName { get; set; }
        /// <summary>
        /// 请求方式
        /// </summary>
        public string RequestMethod { get; set; }
        /// <summary>
        /// api地址(相对路径)
        /// </summary>
        public string ApiUrl { get; set; }
        /// <summary>
        /// 参数
        /// </summary>
        public List<ModelPropertyDescribe> ModelPropertyParameterDescribes { get; set; } = new List<ModelPropertyDescribe>();

        /// <summary>
        /// 参数string
        /// </summary>
        public string ParameterString { get; set; }
        /// <summary>
        /// 返回值
        /// </summary>
        public string ReturnValue { get; set; }
        /// <summary>
        /// 描述
        /// </summary>
        public string Describe { get; set; }
        /// <summary>
        /// 拼接好
        /// </summary>
        public string Describes { get; set; }

        /// <summary>
        /// 是否是文件下载接口
        /// </summary>
        public bool IsDownload { get; set; } = false;
        /// <summary>
        /// 是否是文件导入接口
        /// </summary>
        public bool IsImport { get; set; } = false;

    }

    public class DicCommon
    {
        public static Dictionary<string, SourceType> SourceTypeDic { get; set; } = new Dictionary<string, SourceType>
        {
            ["Path"] = SourceType.Path,
            ["Body"] = SourceType.Body,
            ["Query"] = SourceType.Query
        };
    }
    public class ApiInfo
    {
        /// <summary>
        /// 描述
        /// </summary>
        public string Describe { get; set; }
        /// <summary>
        /// 控制器名
        /// </summary>
        public string ControllerName { get; set; }
        /// <summary>
        /// action名
        /// </summary>
        public string ActionName { get; set; }
        /// <summary>
        /// 请求方式
        /// </summary>
        public string RequestMethod { get; set; }
        /// <summary>
        /// api地址(相对路径)
        /// </summary>
        public string ApiUrl { get; set; }
        /// <summary>
        /// 返回类型描述
        /// </summary>
        public ReturnInfo ReturnInfo { get; set; }
        /// <summary>
        /// 参数描述
        /// </summary>
        public List<ParameterInfo> ParameterInfos { get; set; }
        /// <summary>
        /// 是否文件导入接口
        /// </summary>
        public bool IsImport { get; set; } = false;
        /// <summary>
        /// 是否文件下载接口
        /// </summary>
        public bool IsDownload { get; set; } = false;
    }
    public enum SourceType
    {
        Query = 0,
        Path = 1,
        Body = 2
    }
    /// <summary>
    /// 参数信息
    /// </summary>
    public class ParameterInfo
    {
        /// <summary>
        /// 来源
        /// </summary>
        public SourceType SourceType { get; set; }
        /// <summary>
        /// 参数名
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 默认值
        /// </summary>
        public object DefaultValue { get; set; }
        /// <summary>
        /// 是否验证
        /// </summary>
        public bool IsRequired { get; set; }
        /// <summary>
        /// 类型
        /// </summary>
        public Type ParameteType { get; set; }
    }
    /// <summary>
    /// 返回信息
    /// </summary>
    public class ReturnInfo
    {
        /// <summary>
        /// 返回值例如 arry<SsSsS>
        /// </summary>
        public string ReturnValue { get; set; }
        /// <summary>
        /// 返回类型
        /// </summary>
        public Type ReturnType { get; set; }
    }

    public static class Extendss
    {
        public static IEnumerable<TSource> DistinctEx<TSource, TKey>(
        this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector)
        {
            var hashSet = new HashSet<TKey>();

            foreach (TSource element in source)
            {
                if (hashSet.Add(keySelector(element)))
                {
                    yield return element;
                }
            }
        }
    }
}
